package org.wattdepot.cli.processor;

import org.wattdepot.client.WattDepotClient;
import org.wattdepot.client.WattDepotClientException;
import org.wattdepot.resource.source.jaxb.Source;
import org.wattdepot.resource.source.jaxb.SourceIndex;
import org.wattdepot.resource.source.jaxb.SourceRef;
import org.wattdepot.util.tstamp.Tstamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.datatype.XMLGregorianCalendar;
import java.util.Scanner;

/**
 * A simple command line interface to WattDepot that shows basic connection and processing.
 * 
 * @author Philip Johnson
 */
public class CommandLineInterface {
  /** The URL to the public WattDepot Server. */
  protected String hostUri = "http://server.wattdepot.org:8182/wattdepot/";
  /** Holds an instance of the WattDepot client. */
  protected WattDepotClient client;
  /** Carriage return on this operating system. */
  protected String cr = System.getProperty("line.separator");

  /**
   * Connect to the service and execute some simple commands. Returns immediately if a connection to
   * the service could not be obtained.
   */
  public CommandLineInterface() {
    client = new WattDepotClient(this.hostUri);
    // Stop right away if we cannot contact server.
    if (!client.isHealthy()) {
      System.out.println("Failed to connect to " + client.getWattDepotUri());
      return;
    }
    // Indicate successful connection.
    //System.out.println("Connected to " + client.getWattDepotUri());
  }

  /**
   * Returns a string containing a list of all public sources available on this server.
   * 
   * @return A list of sources as a string.
   * @throws WattDepotClientException If problems occur obtaining the list of sources.
   */
  public String getSourcesInfo() throws WattDepotClientException {
    StringBuffer buff = new StringBuffer(20);
    buff.append("All public sources:").append(cr);
    List<Source> sources = client.getSources();
    for (Source source : sources) {
      buff.append(source.getName()).append(cr);
    }
    return buff.toString();
  }

  /**
   * Returns a double indicating the current power generated by a sample source on November 1, 2009.
   * 
   * @param source The source we want the current power from.
   * @return A double indicating power generated.
   * @throws WattDepotClientException If problems occur obtaining the power value.
   */
  public double getPowerGenerated(Source source) throws WattDepotClientException {
    XMLGregorianCalendar now = null;
    try {
      now = Tstamp.makeTimestamp("2009-11-01");
    }
    catch (Exception e) {
      System.out.println("should never happen.");
    }
    return client.getPowerGenerated(source.getName(), now);
  }

  /**
   * Returns a sample Source. It's the first one returned by the WattDepot server in its index of
   * sources.
   * 
   * @return The sample Source.
   * @throws WattDepotClientException If problems occur getting the source index.
   */
  public Source getSampleSource() throws WattDepotClientException {
    SourceIndex index = client.getSourceIndex();
    SourceRef ref = index.getSourceRef().get(0);
    return client.getSource(ref);
  }

  /**
   * The main program that is invoked from 'java -jar wattdepot-cli.jar'.
   * 
   * @param args All args are ignored.
   * @throws WattDepotClientException If anything goes wrong.
   */
  public static void main(String[] args) throws WattDepotClientException {

    try {
      Map<String, WattDepotCliCommand> commandMap = new HashMap<String, WattDepotCliCommand>();

      commandMap.put("help", new HelpCommand());
      commandMap.put("sources", new SourcesCommand());
      commandMap.put("summary", new SummaryCommand());
      commandMap.put("sensordata", new SensorDataCommand());
      commandMap.put("dailysensordata", new DailySensorDataCommand());
      commandMap.put("power", new PowerCommand());
      commandMap.put("powerstats", new PowerStatsCommand());
      commandMap.put("total", new TotalCommand());
      commandMap.put("chart", new ChartCommand());
      commandMap.put("fueltypes", new FuelTypesCommand());
      commandMap.put("totalpower", new TotalPowerCommand());
      commandMap.put("carboncontent", new CarbonContentCommand());
      
      System.out.println("Starting CLI.");

      // Commenting out extraneous printout message

      /*
       * CommandLineInterface cli = new CommandLineInterface();
       * System.out.println(cli.getSourcesInfo()); Source source = cli.getSampleSource(); double
       * power = cli.getPowerGenerated(source);
       * System.out.println(String.format("Source %s is generating %,.0f MW of power on 11/1/2009.",
       * source.getName(), (power / 1000000)));
       */

      System.out.print("> ");
      Scanner input = new Scanner(System.in);
      String inLine = input.nextLine();

      // List<String> commands = new ArrayList<String>();
      String command = "";
      WattDepotCliCommand commandInstance;

      while (!inLine.equalsIgnoreCase("quit")) {
        command = getCommand(inLine).get(0);
        // System.out.println(commands);

        commandInstance = commandMap.get(command);
        if (commandInstance == null) {
          System.out.println("Invalid command: " + command);
        }
        else {
          String output = commandInstance.doCommand(getCommand(inLine));
          System.out.println(output);
        }

        System.out.print("> ");
        inLine = input.nextLine();
      }

    }
    catch (Exception e) {
      System.out.println("Error encountered: " + e);
    }

    System.out.println("Exiting CLI.");
  }

  /**
   * Parses the user's input into a List.
   * 
   * @param inputString the String of the command line arguments
   * @return a List of strings, one token per item on the list
   */
  public static List<String> getCommand(String inputString) {
    List<String> commandList = new ArrayList<String>();
    String[] commands = inputString.split(" ");
    for (String command : commands) {
      commandList.add(command);
    }
    return commandList;
  }

  /**
   * Creates a Time stamp from a string.
   * 
   * @param inputString the string with user input text.
   * @return a XMLGregorianCalendar date usable in other functions.
   */
  public static XMLGregorianCalendar createTstamp(String inputString) {
    XMLGregorianCalendar date = null;
    try {
      date = Tstamp.makeTimestamp(inputString);
    }
    catch (Exception e) {
      System.out.println("Incorrect Timestamp input. Error encountered: " + e);
      return null;
    }
    return date;
  }
}